package main;

// Nikolas Wolfe
// 2/9/2009
// CIS 4930
// Towers of Hanoi

/*
 * This class contains the Towers and the disks and the
 * accessor/mutator methods required to move them around.
 * The operating threads will utilize this class to do their
 * physical work
 */

public class TowersOfHanoi
{
	private HanoiTower towerA;									// only HanoiTower A is initiailized in constructor
	private HanoiTower towerB = new HanoiTower(0, "Tower B");	// HanoiTower B starts with zero
	private HanoiTower towerC = new HanoiTower(0, "Tower C"); 	// HanoiTower C starts with zero
	
	// constructs a 3 tower problem setup
	public TowersOfHanoi(Integer numDisks)
	{
		this.towerA = new HanoiTower(numDisks, "Tower A");
		towerA.setRightTower(towerB);
		towerA.setLeftTower(towerC);
		
		towerB.setRightTower(towerC);
		towerB.setLeftTower(towerA);
		
		towerC.setRightTower(towerA);
		towerC.setLeftTower(towerB);
		
		System.out.println("Puzzle initialized with " + numDisks + " disks"); 
	}
	
	public synchronized boolean hasLegalMove(HanoiTower tower, String actor)
			throws InterruptedException
	{
		if (tower.peekTowerValue() == 0)
		{
			// executing thread's tower is empty, so wait
			System.out.println(actor + " has nothing to do...");
			this.wait();
			return false;
		} else
		{
			/*
			 *  gigantic logic statement -- four possibilities for success
			 *  1.) tower to the left has a greater top value than current tower
			 *  2.) tower to the right has a greater top value than current tower
			 *  3.) tower to the left is empty
			 *  4.) tower to the right is empty
			 */
			if ( 	tower.getLeftTower().peekTowerValue() > tower.peekTowerValue()
					|| tower.getRightTower().peekTowerValue() > tower.peekTowerValue()
					|| tower.getLeftTower().isEmpty() 
					|| tower.getRightTower().isEmpty())
			{
				// thread has the possibility of taking some action
				return true;
			} else
			{
				// executing thread has no legal moves
				System.out.println(actor + " has nothing to do...");
				this.wait();
				return false;
			}
		}
	}
	
	// this manages the action of notifying other threads that an executing
	// tower watcher is done taking disks off the specified tower and is ready
	// to begin waiting
	public synchronized void checkReadyToYield( HanoiTower tower ) throws InterruptedException
	{
		if( tower.isReadyToYield() )
		{
			this.notifyAll(); // tell someone else they can go
		}
	}
	
	public synchronized boolean moveDiskLeft( HanoiTower tower, String actor ) 
	{
		// move top Disk on the stack to the tower on the left
		if( !tower.isEmpty() )
		{
			// replace disk if the push fails
			Disk temp = tower.popDisk();
			if( !tower.getLeftTower().pushDisk( temp ) )
			{
				tower.pushDisk(temp);
				// supressing output:
				// System.out.println(actor + " attempted an illegal move! Cannot put disk " + 
				//		temp.toString() + " on top of disk " + 
				//		tower.getLeftTower().peekTowerValue());
				return false;
			}
			// output state
			System.out.println(actor + " moved disk " + temp.getValue() + 
					" onto " + tower.getLeftTower().getName() +
					"\nCurrent State: " + toString());
			
			// set disk properties
			tower.getLeftTower().peekTower().setPrevTower(tower);
			tower.getLeftTower().peekTower().setPrevDisk(tower.peekTower());
			
			return true;
		}
		else { return false; }
	}
	
	public synchronized boolean moveDiskRight( HanoiTower tower, String actor )
	{
		// move top Disk on the stack to the tower on the left
		if( !tower.isEmpty() )
		{
			// replace disk if the push fails
			Disk temp = tower.popDisk();
			if( !tower.getRightTower().pushDisk( temp ))
			{
				tower.pushDisk(temp);
				// supressing output:
				// System.out.println(actor + " attempted an illegal move! Cannot put disk " + 
				//		temp.toString() + " on top of disk " + 
				//		tower.getRightTower().peekTowerValue());
				return false;
			}
			// output state
			System.out.println(actor + " moved disk " + temp.getValue() + 
					" onto " + tower.getRightTower().getName() +
					"\nCurrent State: " + toString());
			
			// set disk properties
			tower.getRightTower().peekTower().setPrevTower(tower);
			tower.getRightTower().peekTower().setPrevDisk(tower.peekTower());
			
			return true;
		}
		else { return false; }
	}
	
	// checks that first two Towers are empty and C is not
	public synchronized boolean checkIfSolved()
	{
		if( towerA.isEmpty() && ( towerB.isEmpty() || towerC.isEmpty() ))
		{
			System.out.println("Puzzle solved! Final state: " + toString());
			Thread.currentThread().interrupt(); // kill the executing thread
			this.notifyAll(); // awake any waiting threads
			return true;
		}
		else { return false; }
	}
	
	public String toString()
	{
		return "[" + towerA.toString() + ":" + towerB.toString() + 
		":" + towerC.toString() + "]";
	}
	
	/* ---------------
	 * Tower Accessors 
	 * ---------------
	 * try not to use these too much!
	 */
	public HanoiTower getTowerA()
	{
		return towerA;
	}

	public HanoiTower getTowerB()
	{
		return towerB;
	}

	public HanoiTower getTowerC()
	{
		return towerC;
	}
}
